﻿<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<style>
body {padding:0px}
</style>
</head>
<body>
<script>
/**
 Copyright (c) 2010 LungZeno

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use,
 copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following
 conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.

**/

$(function($){
  function HCF(n0,n1){
    var dividend,divisor,remainder;
    if(Math.abs(n0) < Math.abs(n1)){
      divisor = n1;
      remainder = n0;
    }else{
      divisor = n0;
      remainder = n1;
    }
    while(remainder){
      dividend = divisor;
      divisor = remainder;
      remainder = dividend % divisor;
    }
    return Math.abs(divisor);
  }

  function Rational(numerator,denominator){
    if(Math.floor(numerator) !== numerator || Math.floor(denominator) !== denominator)
      throw new Error("wrong argument format!");
    if(numerator > 9007199254740992 || numerator < -9007199254740992
       || denominator > 9007199254740992 || denominator < -9007199254740992)
      throw new OverflowError("overflow!");
    this.numerator = numerator;
    this.denominator = denominator;
  }
  Rational.prototype = {
    numerator: undefined,
    denominator: undefined,
    add: function add(r){
      var hcf,thisDenominator,rDenominator;
      var numerator, denominator;
      if(this.denominator !== 0 && r.denominator !== 0){
        hcf = HCF(this.denominator,r.denominator);
        thisDenominator = this.denominator / hcf;
        rDenominator = r.denominator / hcf;
        if(Rational._checkMulOverflow(this.numerator, rDenominator) ||
           Rational._checkMulOverflow(r.numerator, thisDenominator) ||
           Rational._checkAddOverflow(this.numerator * rDenominator, r.numerator * thisDenominator) ||
           Rational._checkMulOverflow(this.denominator, rDenominator))
          throw new OverflowError("overflow!");
        numerator = this.numerator * rDenominator + r.numerator * thisDenominator;
        denominator = thisDenominator * hcf * rDenominator;
      } else {
        numerator = this.numerator * r.denominator + r.numerator * this.denominator;
        denominator = this.denominator * r.denominator;
      }      

      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    sub: function sub(r){
      var hcf,thisDenominator,rDenominator;
      var numerator, denominator;
      if(this.denominator !== 0 && r.denominator !== 0){
        hcf = HCF(this.denominator,r.denominator);
        thisDenominator = this.denominator / hcf;
        rDenominator = r.denominator / hcf;
        if(Rational._checkMulOverflow(this.numerator, rDenominator) ||
           Rational._checkMulOverflow(r.numerator, thisDenominator) ||
           Rational._checkSubOverflow(this.numerator * rDenominator, r.numerator * thisDenominator) ||
           Rational._checkMulOverflow(this.denominator, rDenominator))
          throw new OverflowError("overflow!");
        numerator = this.numerator * rDenominator - r.numerator * thisDenominator;
        denominator = thisDenominator * hcf * rDenominator;
      } else {
        numerator = this.numerator * r.denominator - r.numerator * this.denominator;
        denominator = this.denominator * r.denominator;
      }      

      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    mul: function mul(r){
      var hcf0,hcf1,thisDenominator,rNumerator,thisNumerator,rDenominator;
      var numerator, denominator;
      if(this.denominator !== 0 && r.denominator !== 0 && this.numerator !== 0 && r.numerator !== 0){
        hcf0 = HCF(this.denominator,r.numerator);
        thisDenominator = this.denominator / hcf0;
        rNumerator = r.numerator / hcf0;
        hcf1 = HCF(this.numerator,r.denominator);
        thisNumerator = this.numerator / hcf1;
        rDenominator = r.denominator / hcf1;
        if(Rational._checkMulOverflow(thisNumerator, rNumerator) ||
           Rational._checkMulOverflow(thisDenominator, rDenominator))
          throw new OverflowError("overflow!");
        numerator = thisNumerator * rNumerator;
        denominator = thisDenominator * rDenominator;
      } else {
        numerator = this.numerator * r.numerator;
        denominator = this.denominator * r.denominator;
      }      

      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    div: function div(r){
      var hcf0,hcf1,thisDenominator,rNumerator,thisNumerator,rDenominator;
      var numerator, denominator;
      if(this.denominator !== 0 && r.denominator !== 0 && this.numerator !== 0 && r.numerator !== 0){
        hcf0 = HCF(this.denominator,r.denominator);
        thisDenominator = this.denominator / hcf0;
        rDenominator = r.denominator / hcf0;
        hcf1 = HCF(this.numerator,r.numerator);
        thisNumerator = this.numerator / hcf1;
        rNumerator = r.numerator / hcf1;
        if(Rational._checkMulOverflow(thisNumerator, rDenominator) ||
           Rational._checkMulOverflow(thisDenominator, rNumerator))
          throw new OverflowError("overflow!");
        numerator = thisNumerator * rDenominator;
        denominator = thisDenominator * rNumerator;
      } else {
        numerator = this.numerator * r.denominator;
        denominator = this.denominator * r.numerator;
      }      

      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    mod: function mod(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator) ||
         Rational._checkMulOverflow(this.denominator, r.denominator))
        throw new OverflowError("overflow!");
      var numerator = this.numerator * r.denominator % (r.numerator * this.denominator);
      var denominator = this.denominator * r.denominator;
      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    addInt: function addInt(n){
      if(Rational._checkMulOverflow(n, this.denominator) ||
         Rational._checkAddOverflow(this.numerator, n * this.denominator))
        throw new OverflowError("overflow!");
      var numerator = this.numerator + n * this.denominator;
      var denominator = this.denominator;
      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    subInt: function subInt(n){
      if(Rational._checkMulOverflow(n, this.denominator) ||
         Rational._checkSubOverflow(this.numerator, n * this.denominator))
        throw new OverflowError("overflow!");
      var numerator = this.numerator - n * this.denominator;
      var denominator = this.denominator;
      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    mulInt: function mulInt(n){
      var hcf,thisDenominator;
      var numerator, denominator;
      if(this.denominator !== 0 && n !== 0){
        hcf = HCF(this.denominator,n);
        thisDenominator = this.denominator / hcf;
        n = n / hcf;
        if(Rational._checkMulOverflow(this.numerator, n))
          throw new OverflowError("overflow!");
        numerator = this.numerator * n;
        denominator = thisDenominator;
      } else {
        numerator = this.numerator * n;
        denominator = this.denominator;
      }      

      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    divInt: function divInt(n){
      var hcf,thisNumerator;
      var numerator, denominator;
      if(this.numerator !== 0 && n !== 0){
        hcf = HCF(this.numerator,n);
        thisNumerator = this.numerator / hcf;
        n = n / hcf;
        if(Rational._checkMulOverflow(this.denominator, n))
          throw new OverflowError("overflow!");
        numerator = thisNumerator;
        denominator = this.denominator * n;
      } else {
        numerator = this.numerator;
        denominator = this.denominator * n;
      }      

      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    modInt: function modInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
        throw new OverflowError("overflow!");
      var numerator = this.numerator % (n * this.denominator);
      var denominator = this.denominator;
      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    rec: function rec(){
      return new Rational(this.denominator, this.numerator);
    },
    gt: function gt(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() > r.valueOf() :
             r.denominator > 0 === this.denominator > 0 ? 
             this.numerator * r.denominator > r.numerator * this.denominator :
             this.numerator * r.denominator < r.numerator * this.denominator;
    },
    ge: function ge(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() >= r.valueOf() :
             r.denominator > 0 === this.denominator > 0 ? 
             this.numerator * r.denominator >= r.numerator * this.denominator :
             this.numerator * r.denominator <= r.numerator * this.denominator;
    },
    lt: function lt(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() < r.valueOf() :
             r.denominator > 0 === this.denominator > 0 ? 
             this.numerator * r.denominator < r.numerator * this.denominator :
             this.numerator * r.denominator > r.numerator * this.denominator;
    },
    le: function le(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() <= r.valueOf() :
             r.denominator > 0 === this.denominator > 0 ? 
             this.numerator * r.denominator <= r.numerator * this.denominator :
             this.numerator * r.denominator >= r.numerator * this.denominator;
    },
    eq: function eq(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() === r.valueOf() :
             this.numerator * r.denominator === r.numerator * this.denominator;
    },
    ne: function ne(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() !== r.valueOf() :
             this.numerator * r.denominator !== r.numerator * this.denominator;
    },
    gtInt: function gtInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() > n :
             this.denominator > 0 ? 
             this.numerator > n * this.denominator :
             this.numerator < n * this.denominator;
    },
    geInt: function geInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() >= n :
             this.denominator > 0 ? 
             this.numerator >= n * this.denominator :
             this.numerator <= n * this.denominator;
    },
    ltInt: function ltInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() < n :
             this.denominator > 0 ? 
             this.numerator < n * this.denominator :
             this.numerator > n * this.denominator;
    },
    leInt: function leInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() <= n :
             this.denominator > 0 ? 
             this.numerator <= n * this.denominator :
             this.numerator >= n * this.denominator;
    },
    eqInt: function eqInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() === n :
             this.numerator === n * this.denominator;
    },
    neInt: function neInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() !== n :
             this.numerator !== n * this.denominator;
    },
    toString: function toString(){
      return this.numerator + ' / ' + this.denominator;
    },
    valueOf: function valueOf(){
      return this.numerator / this.denominator;
    }
  };

  Rational.max = function max(){
    return Array.prototype.slice.call(arguments,0,arguments.length).sort(function(a,b){
      if(Rational._checkMulOverflow(b.numerator, a.denominator) ||
         Rational._checkMulOverflow(a.numerator, b.denominator) ||
         Rational._checkSubOverflow(b.numerator * a.denominator, a.numerator * b.denominator))
         throw new OverflowError("overflow!");
      return b.numerator * a.denominator - a.numerator * b.denominator;
    })[0];
  };

  Rational.min = function min(){
    return Array.prototype.slice.call(arguments,0,arguments.length).sort(function(a,b){
      if(Rational._checkMulOverflow(a.numerator, b.denominator) ||
         Rational._checkMulOverflow(b.numerator, a.denominator) ||
         Rational._checkSubOverflow(a.numerator * b.denominator, b.numerator * a.denominator))
         throw new OverflowError("overflow!");
      return a.numerator * b.denominator - b.numerator * a.denominator;
    })[0];
  };

  Rational.floorInt = function floorInt(r){
    return r.numerator === 0 || r.denominator === 0 ? r.valueOf() :
           (r.numerator - r.numerator % r.denominator) / r.denominator -
           (r.numerator / 0 === -r.denominator / 0 && r.numerator % r.denominator ? 1 : 0);
  };

  Rational.roundInt = function roundInt(r){
    return r.numerator === 0 || r.denominator === 0 ? r.valueOf() :
           (r.numerator - r.numerator % r.denominator) / r.denominator -
           (r.numerator / 0 === -r.denominator / 0 && r.numerator % r.denominator ? 1 : 0) +
    // avoid overflow , not use a * 2 - b, use a - b + a
           ((r.denominator < 0 ? -r.numerator : r.numerator) % r.denominator - (r.numerator < 0 ? -r.denominator : r.denominator) + (r.denominator < 0 ? -r.numerator : r.numerator) % r.denominator >= 0);
  };

  Rational._checkAddOverflow = function _checkAddOverflow(r0,r1){
    if(r0 > 0 && r1 > 0)
      return 9007199254740992 - r0 < r1;
    else if(r0 < 0 && r1 < 0)
      return -9007199254740992 + r0 > r1;
    return false;
  };

  Rational._checkSubOverflow = function _checkSubOverflow(r0,r1){
    return Rational._checkAddOverflow(r0, -r1);
  };

  Rational._checkMulOverflow = function _checkMulOverflow(r0,r1){
    if(r0 === 0 || r1 === 0)
      return false;
    if(r0 < 0)
      r0 = -r0;
    if(r1 < 0)
      r1 = -r1;
    var q = (9007199254740992 - 9007199254740992 % r0) / r0;
    if(q < r1 || (q === r1 && 9007199254740992 % r0 !== 0))
      return true;
    else
      return false;
  };

  function checkStringOverflow(){
    for(var i = 0; i < arguments.length; ++i){
      var s = arguments[i];
      if(s.length > "9007199254740992".length || s.length === "9007199254740992".length && s > "9007199254740992")
        return true;
    }
    return false;
  }

  function RGBtoArcHue(red,green,blue){
    if(typeof red==="number")
      red = new Rational(red,1);
    if(typeof green==="number")
      green = new Rational(green,1);
    if(typeof blue==="number")
      blue = new Rational(blue,1);
    return (Math.atan2(green.sub(blue).divInt(2).valueOf()*Math.sqrt(3),red.sub(green.divInt(2)).sub(blue.divInt(2)).valueOf()) * 180 / Math.PI + 360) % 360;
  }

  function RGBtoHSY(r,g,b,red,green,blue,isCylinder){
    if(typeof r==="number")
      r = new Rational(r,1);
    if(typeof g==="number")
      g = new Rational(g,1);
    if(typeof b==="number")
      b = new Rational(b,1);
    if(typeof red==="number")
      red = new Rational(red,1);
    if(typeof green==="number")
      green = new Rational(green,1);
    if(typeof blue==="number")
      blue = new Rational(blue,1);
    var max = Rational.max(red,green,blue);
    var min = Rational.min(red,green,blue);
    var C = max.sub(min);
    var H;
    if(C.eqInt(0))
      H = new Rational(0,1);
    else if(max.eq(red))
      H = green.sub(blue).mulInt(60).div(C).addInt(360).modInt(360);
    else if(max.eq(green))
      H = blue.sub(red).div(C).addInt(2).mulInt(60);
    else if(max.eq(blue))
      H = red.sub(green).div(C).addInt(4).mulInt(60);
    var luma = r.mul(red).add(g.mul(green)).add(b.mul(blue)).divInt(255);
    var S;
    if(isCylinder){
      var lumaWhenMaxC;
      switch(Math.floor(H.divInt(60).valueOf())){
        case 0:
          lumaWhenMaxC = r.add(g.mul(H.modInt(60).divInt(60)));
          break;
        case 1:
          lumaWhenMaxC = g.add(r.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
          break;
        case 2:
          lumaWhenMaxC = g.add(b.mul(H.modInt(60).divInt(60)));
          break;
        case 3:
          lumaWhenMaxC = b.add(g.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
          break;
        case 4:
          lumaWhenMaxC = b.add(r.mul(H.modInt(60).divInt(60)));
          break;
        case 5:
          lumaWhenMaxC = r.add(b.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
          break;
      }
      if(luma.le(lumaWhenMaxC))
        S = new Rational(1,1).sub(min.divInt(255).div(luma));
      else
        S = max.divInt(255).sub(luma).div(new Rational(1,1).sub(luma));
    } else
      S = new Rational(1,1).sub(min.divInt(255).div(luma));
    if(isNaN(H.valueOf()))
      H = new Rational(0,1);
    if(isNaN(S.valueOf()))
      S = new Rational(0,1);
    return [H,S,luma];
  }

  function OverflowError(message){
    this.message = message;
  }
  OverflowError.prototype = new Error('prototype');

  function InvalidRangeError(message){
    this.message = message;
  }
  InvalidRangeError.prototype = new Error('prototype');

  function InvalidFormatError(message){
    this.message = message;
  }
  InvalidFormatError.prototype = new Error('prototype');

  function HSYtoRGB(r,g,b,H,S,luma,isCylinder){
    if(typeof r==="number")
      r = new Rational(r,1);
    if(typeof g==="number")
      g = new Rational(g,1);
    if(typeof b==="number")
      b = new Rational(b,1);
    if(typeof H==="number")
      H = new Rational(H,1);
    if(typeof S==="number")
      S = new Rational(S,1);
    if(typeof luma==="number")
      luma = new Rational(luma,1);
    var lumaWhenMaxC;
    H = H.modInt(360).addInt(360).modInt(360);
    switch(Math.floor(H.divInt(60).valueOf())){
      case 0:
      case 6:
        lumaWhenMaxC = r.add(g.mul(H.modInt(60).divInt(60)));
        break;
      case 1:
        lumaWhenMaxC = g.add(r.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
        break;
      case 2:
        lumaWhenMaxC = g.add(b.mul(H.modInt(60).divInt(60)));
        break;
      case 3:
        lumaWhenMaxC = b.add(g.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
        break;
      case 4:
        lumaWhenMaxC = b.add(r.mul(H.modInt(60).divInt(60)));
        break;
      case 5:
        lumaWhenMaxC = r.add(b.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
        break;
    }
    if(!isCylinder && S.gt(lumaWhenMaxC.mul(new Rational(1,1).sub(luma).div(new Rational(1,1).sub(lumaWhenMaxC).mul(luma)))))
      throw new InvalidRangeError('invalid range!');
    var min,max,C;
    if(!isCylinder || luma.le(lumaWhenMaxC)){
//      var min = (255 - S) * I / 255;
      min = new Rational(1,1).sub(S).mul(luma).mulInt(255);
//      var C = S * I / 255 / lumaWhenMaxC;
      C = S.mul(luma).mulInt(255).div(lumaWhenMaxC);
//      var max = C + min;
      max = C.add(min);
    } else {
//      var max = S * (255 - I) / 255 + I;
      max = S.mul(new Rational(1,1).sub(luma)).add(luma).mulInt(255);
//      var C = S * (255 - I) / 255 / (1 - lumaWhenMaxC);
      C = S.mul(new Rational(1,1).sub(luma)).mulInt(255).div(new Rational(1,1).sub(lumaWhenMaxC));
//      var min = max - C;
      min = max.sub(C);
    }
    var red, green, blue;
    switch(Math.floor(H.divInt(60).valueOf())){
      case 0:
        red = max;
        blue = min;
//        green = (I - r * red - b * blue) / g;
        green = luma.mulInt(255).sub(r.mul(red)).sub(b.mul(blue)).div(g);
        break;
      case 1:
        green = max;
        blue = min;
//        red = (I - g * green - b * blue) / r;
        red = luma.mulInt(255).sub(g.mul(green)).sub(b.mul(blue)).div(r);
        break;
      case 2:
        green = max;
        red = min;
//        blue = (I - r * red - g * green) / b;
        blue = luma.mulInt(255).sub(r.mul(red)).sub(g.mul(green)).div(b);
        break;
      case 3:
        blue = max;
        red = min;
//        green = (I - r * red - b * blue) / g;
        green = luma.mulInt(255).sub(r.mul(red)).sub(b.mul(blue)).div(g);
        break;
      case 4:
        blue = max;
        green = min;
//        red = (I - g * green - b * blue) / r;
        red = luma.mulInt(255).sub(g.mul(green)).sub(b.mul(blue)).div(r);
        break;
      case 5:
        red = max;
        green = min;
//        blue = (I - r * red - g * green) / b;
        blue = luma.mulInt(255).sub(r.mul(red)).sub(g.mul(green)).div(b);
        break;
    }
    return [red,green,blue];
  }

  function RGBtoHSV(red,green,blue){
    if(typeof red==="number")
      red = new Rational(red,1);
    if(typeof green==="number")
      green = new Rational(green,1);
    if(typeof blue==="number")
      blue = new Rational(blue,1);
    var max = Rational.max(red,green,blue);
    var min = Rational.min(red,green,blue);
    var C = max.sub(min);
    var H;
    if(C.eqInt(0))
      H = new Rational(0,1);
    else if(max.eq(red))
      H = green.sub(blue).mulInt(60).div(C).addInt(360).modInt(360);
    else if(max.eq(green))
      H = blue.sub(red).div(C).addInt(2).mulInt(60);
    else if(max.eq(blue))
      H = red.sub(green).div(C).addInt(4).mulInt(60);
    var V = max.divInt(255);
    var S = C.div(max);
    return [H,S,V];
  }

  function HSVtoRGB(H,S,V){
    if(typeof H==="number")
      H = new Rational(H,1);
    if(typeof S==="number")
      S = new Rational(S,1);
    if(typeof V==="number")
      V = new Rational(V,1);
    var C = S.mul(V).mulInt(255);
    var max = V.mulInt(255);
    var min = max.sub(C);
    var red, green, blue;
    H = H.modInt(360).addInt(360).modInt(360);
    if(H.geInt(300) || H.lt(60)){
      red = max;
      blue = min;
      var temp = H.divInt(60);
      if(H.geInt(300))
        temp = temp.sub(6);
      green = temp.mul(C).add(blue);
    } else if(H.geInt(60) && H.ltInt(180)){
      green = max;
      red = min;
      blue = H.divInt(60).subInt(2).mul(C).add(red);
    } else if(H.geInt(180) && H.lt(300)){
      blue = max;
      green = min;
      red = H.divInt(60).subInt(4).mul(C).add(green);
    }
    return [red,green,blue];
  }

  function RGBtoHSL(red,green,blue){
    if(typeof red==="number")
      red = new Rational(red,1);
    if(typeof green==="number")
      green = new Rational(green,1);
    if(typeof blue==="number")
      blue = new Rational(blue,1);
    var max = Rational.max(red,green,blue);
    var min = Rational.min(red,green,blue);
    var C = max.sub(min);
    var H;
    if(C.eqInt(0))
      H = new Rational(0,1);
    else if(max.eq(red))
      H = green.sub(blue).mulInt(60).div(C).addInt(360).modInt(360);
    else if(max.eq(green))
      H = blue.sub(red).div(C).addInt(2).mulInt(60);
    else if(max.eq(blue))
      H = red.sub(green).div(C).addInt(4).mulInt(60);
    var L = max.add(min).divInt(2).divInt(255);
    var S;
    if(L.le(new Rational(1,2)))
      S = C.divInt(255).div(L).divInt(2);
    else
      S = C.divInt(255).div(new Rational(1,1).sub(L)).divInt(2);
    return [H,S,L];
  }

  function HSLtoRGB(H,S,L){
    if(typeof H==="number")
      H = new Rational(H,1);
    if(typeof S==="number")
      S = new Rational(S,1);
    if(typeof L==="number")
      L = new Rational(L,1);
    var C;
    if(L.le(new Rational(1,2)))
      C = S.mulInt(2).mul(L).mulInt(255);
    else
      C = S.mulInt(2).mul(new Rational(1,1).sub(L)).mulInt(255);
    var max = C.divInt(2).add(L.mulInt(255));
    var min = max.sub(C);
    var red, green, blue;
    H = H.modInt(360).addInt(360).modInt(360);
    if(H.geInt(300) || H.lt(60)){
      red = max;
      blue = min;
      var temp = H.divInt(60);
      if(H.geInt(300))
        temp = temp.sub(6);
      green = temp.mul(C).add(blue);
    } else if(H.geInt(60) && H.ltInt(180)){
      green = max;
      red = min;
      blue = H.divInt(60).subInt(2).mul(C).add(red);
    } else if(H.geInt(180) && H.lt(300)){
      blue = max;
      green = min;
      red = H.divInt(60).subInt(4).mul(C).add(green);
    }
    return [red,green,blue];
  }

  var bg = $('<div></div>',{id:'bg'}).css({width:'320px',height:'192px',position:'absolute',top:'10px',left:'100px'}).appendTo('body');
  for(var i = 0; i < 34; ++i)
    for(var j = 0; j < 25; ++j){
      $('<div></div>').css({width:'8px',height:'8px',position:'absolute',top:j*8+'px',left:i*8+'px',backgroundColor:(i + j)%2?'#ff0000':'#ffffff'}).appendTo(bg);
    }

  var bg2 = $('<div></div>',{id:'bg2'}).css({width:'500px',height:'300px',position:'absolute',top:'210px',left:'100px'}).appendTo('body');

  var bg3 = $('<div></div>',{id:'bg3'}).css({width:'100px',height:'300px',position:'absolute',top:'210px',left:'0px'}).appendTo('body');

  function fillColor(){
    try{
      var val = $('#fullInput').val();
      if(val.match(/^\s*\#[\da-f]{6}(?:(?:\s+|\s*\,\s*)\#[\da-f]{6}|\s*\,){0,13}\s*$/i)){
        var pattern = /(?:\,\s*)?(\#[\da-f]{6})|\,/ig;
        var result, i=0;
        while(result = pattern.exec(val)){
          if(result[0].match(/\#[\da-f]{6}/i))
            $('#input'+i).val(result[1]);
          else
            $('#input'+i).val('');
          ++i;
        }
        for(var j = i; j < 14; ++j)
          $('#input'+j).val('');
        var oldSlientError = slientError;
        slientError = true;
        for(var i = 0; i < 14; ++i)
          changeFromHex.apply($('#input'+i)[0],arguments);
        slientError = oldSlientError;
      } else
        throw new InvalidFormatError('invalid format! e.g. of well format is: #FFFFFF #5568EF');
    }catch(e){
      if(e instanceof InvalidFormatError){
        if(!slientError)
          alert(e.message);
      } else
        throw e;
    }
  }

  function fullInputChange(){
    changeAllToFalse();
    if(autoChange)
        fillColor.apply(this,arguments);
  }

  function updateFullInput(){
    var text = '';
    var j = 0;
    for(var i = 0; i < 14; ++i){
      var result = $.trim($('#input'+i).val()).match(/^\#[\da-f]{6}$/i);
      if(result){
        text += (i === 0 ? '' : j > 1 ? Array(j+1).join(',') : ' ') + result[0];
        j = 1;
      } else
        ++j;
    }
    $('#fullInput').val(text);
  }

  $('<div></div>',{text:'many colors'}).css({textAlign:'right',fontSize:'16px',width:'95px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'0px',right:'0px'}).appendTo(bg3));
  $('<input />',{type:'text',id:'fullInput'}).css({width:'65.8em',position:'absolute',top:'0px',left:'0px'}).appendTo(bg2).change(fullInputChange);

  $('<div></div>',{text:'(in hex) color'}).css({textAlign:'right',fontSize:'16px',width:'95px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'2em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'red'}).css({fontSize:'16px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'4em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'green'}).css({fontSize:'16px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'6em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'blue'}).css({fontSize:'16px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'8em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'fraction'}).css({borderTop:'1px solid black',width:'95px',fontSize:'16px',position:'absolute',top:'0px',left:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',width:'100px',position:'absolute',top:'10em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'chord hue',id:'out0label'}).css({textAlign:'right',fontSize:'16px',width:'95px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'11em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'saturation',id:'out1label'}).css({textAlign:'right',fontSize:'16px',width:'95px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'13em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'luma',id:'out2label'}).css({textAlign:'right',fontSize:'16px',width:'95px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'15em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'decimal'}).css({borderTop:'1px solid black',width:'95px',fontSize:'16px',position:'absolute',top:'0px',left:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',width:'100px',position:'absolute',top:'17em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'arc hue',id:'out3label'}).css({textAlign:'right',fontSize:'16px',width:'95px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'18em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'chord hue',id:'out4label'}).css({textAlign:'right',fontSize:'16px',width:'95px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'20em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'saturation',id:'out5label'}).css({textAlign:'right',fontSize:'16px',width:'95px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'22em',right:'0px'}).appendTo(bg3));
  $('<div></div>',{text:'luma',id:'out6label'}).css({textAlign:'right',fontSize:'16px',width:'95px',position:'absolute',top:'1px',right:'5px'}).appendTo($('<div></div>').css({fontSize:'13.4px',position:'absolute',top:'24em',right:'0px'}).appendTo(bg3));

  var bg4 = $('<div></div>',{id:'bg4'}).css({width:'535px',height:'152px',position:'absolute',top:'10px',left:'430px'}).appendTo('body');

  function setButton(option,click){
    if(!click)
      click = function(){};
    var inner = $('<div></div>').css({position:'absolute',borderStyle:'none',padding:'1px'}).mouseenter(function(){
      $(this).css({border:'1px dashed gray',padding:'0px'});
    }).mouseleave(function(){
      $(this).css({borderStyle:'none',padding:'1px'});
    }).append(
    $('<div></div>').css({padding:'0px',borderWidth:'8px',borderStyle:'solid',borderColor:'#5656AB #7070E0 #7070E0 #5656AB'}).click(function(){
//    $('<div></div>').css({padding:'0px',borderWidth:'8px',borderStyle:'solid',borderColor:'#BCBCFF #5656AB #5656AB #BCBCFF'}).click(function(){
//    $('<div></div>').css({padding:'0px',borderWidth:'8px',borderStyle:'solid',borderColor:'#20FF20 #2F6D2F #2F6D2F #20FF20'}).click(function(){
      var button = $(this).children();
      button.data('set', button.data('set') === null ? false : !button.data('set'));
      if(button.data('set'))
        button.css({backgroundColor:'#45458A'})
        .parent().css({borderColor:'#5656AB #7070E0 #7070E0 #5656AB'});
//        button.css({backgroundColor:'#305430'})
//        .parent().css({borderColor:'#2F6D2F #1B9C1B #1B9C1B #2F6D2F'});
      else
        button.css({backgroundColor:'#ABABFF'})
        .parent().css({borderColor:'#BCBCFF #5656AB #5656AB #BCBCFF'})
//        button.css({backgroundColor:'#00F600'})
//        .parent().css({borderColor:'#20FF20 #2F6D2F #2F6D2F #20FF20'})
      click.apply(this,arguments);
    }).mousedown(function(){
      var button = $(this).children();
      button.css({backgroundColor:'#45458A'})
      .parent().css({borderColor:'#5656AB #7070E0 #7070E0 #5656AB'});
//      button.css({backgroundColor:'#305430'})
//      .parent().css({borderColor:'#2F6D2F #1B9C1B #1B9C1B #2F6D2F'});
    }).append($('<button></button>',option).css({margin:'0px',textAlign:'center',borderStyle:'none',backgroundColor:'#45458A',color:'yellow'}))).appendTo($('body'));
//    }).append($('<button></button>',option).css({margin:'0px',textAlign:'center',borderStyle:'none',backgroundColor:'#ABABFF',color:'yellow'}))).appendTo($('body'));
//    }).append($('<button></button>',option).css({margin:'0px',textAlign:'center',borderStyle:'none',backgroundColor:'#00F600',color:'yellow'}))).appendTo($('body'));
    return $('<div></div>').css({position:'relative',width:inner.outerWidth()+'px',height:inner.outerHeight()+'px'}).append(inner);
  }

  var autoChange = true;

  setButton({id:'autoChange',text:'autoChange'},function(){
    autoChange = $(this).children().data('set');
    changeAllToFalse();
    readyRGBChange[0] = false;
    readyRGBChange[1] = false;
    readyRGBChange[2] = false;
  }).appendTo(bg4).css({'float':'left'});

  var groupChange = true;

  function updateChange(){
    for(var i = 0; i < 14; ++i)
      if(changeFrom[i] === ''){
        $('#out0'+i).val('');
        $('#out1'+i).val('');
        $('#out2'+i).val('');
        $('#out3'+i).val('');
        $('#out4'+i).val('');
        $('#out5'+i).val('');
        $('#out6'+i).val('');
      } else try{
        var result = convert(lastSuccess[i][0], lastSuccess[i][1], lastSuccess[i][2]);
        result[0];
        var arcHue = RGBtoArcHue(lastSuccess[i][0],lastSuccess[i][1],lastSuccess[i][2]);
        $('#out0'+i).val(result[0].toString());
        $('#out1'+i).val(result[1].toString());
        $('#out2'+i).val(result[2].toString());
        $('#out3'+i).val(arcHue);
        $('#out4'+i).val(result[0].valueOf());
        $('#out5'+i).val(result[1].valueOf());
        $('#out6'+i).val(result[2].valueOf());
      } catch(e) {
        if(e instanceof OverflowError || e instanceof InvalidRangeError){
          $('#out0'+i).val('');
          $('#out1'+i).val('');
          $('#out2'+i).val('');
          $('#out3'+i).val('');
          $('#out4'+i).val('');
          $('#out5'+i).val('');
          $('#out6'+i).val('');
        } else
          throw e;
      }
  }

  var readyRGBChange = [false,false,false];

  var lastSuccessRGB = [new Rational(21,100), new Rational(72,100), new Rational(7,100)];

  var lastSuccessRGBChange = false;

  var customChange = false;

  function lastSuccessRGBUpdate(){
    r = lastSuccessRGB[0];
    g = lastSuccessRGB[1];
    b = lastSuccessRGB[2];
    $('#current').text('r = ' + r.toString() + ', g = ' + g.toString() + ', b = ' + b.toString());
  }

  function changeFromRGB(){
    var result0 = $.trim($('#r').val()).match(/^(\d+)\s*\/\s*(\d+)$/);
    var result1 = $.trim($('#g').val()).match(/^(\d+)\s*\/\s*(\d+)$/);
    var result2 = $.trim($('#b').val()).match(/^(\d+)\s*\/\s*(\d+)$/);
    try {
      if(result0 && result1 && result2){
        var overflow0 = checkStringOverflow(result0[1],result0[2]);
        var overflow1 = checkStringOverflow(result1[1],result1[2]);
        var overflow2 = checkStringOverflow(result2[1],result2[2]);
        if(overflow0 || overflow1 || overflow2){
          var idno = overflow0 ? 0 : overflow1 ? 1 : 2;
          var e = new OverflowError("overflow of " + ['r','g','b'][idno] + "!");
          e.nos = [!overflow0,!overflow1,!overflow2];
          throw e;
        }
        var tempR = new Rational(+result0[1],+result0[2]);
        var tempG = new Rational(+result1[1],+result1[2]);
        var tempB = new Rational(+result2[1],+result2[2]);
        if(tempR.add(tempG).add(tempB).ne(new Rational(1,1)))
          throw new InvalidRangeError('invalid Range! r + g + b must be equal to 1');
        lastSuccessRGB[0] = tempR;
        lastSuccessRGB[1] = tempG;
        lastSuccessRGB[2] = tempB;
        lastSuccessRGBUpdate();
        readyRGBChange[0] = false;
        readyRGBChange[1] = false;
        readyRGBChange[2] = false;
        return true;
      } else {
        var text = !result0 ? 'r' : !result1 ? 'g' : 'b';
        var e = new InvalidFormatError("invalid format of " + text + "! e.g. of well format: 27 / 100");
        e.nos = [!!result0,!!result1,!!result2];
        throw e;
      }
    } catch (e) {
      if(e instanceof InvalidRangeError || e instanceof OverflowError){
        if(e.nos){
          readyRGBChange[0] = e.nos[0];
          readyRGBChange[1] = e.nos[1];
          readyRGBChange[2] = e.nos[2];
        }
        alert(e.message);
        return false;
      } else
        throw e;
    }
  }

  function RGBChange(){
    var idno = this.id === 'r' ? 0 : this.id === 'g' ? 1 : 2;
    readyRGBChange[idno] = true;
    if(customChange && autoChange && (!groupChange || readyRGBChange[0] && readyRGBChange[1] && readyRGBChange[2]))
      if(changeFromRGB.apply(this, arguments)){
        updateChangeIfHSY();
      }
  }

  var inChangeNow = false;

  var colorModel = "HSY";

  function updateChangeIfHSY(){
    if(colorModel === "HSY")
      updateChange();
  }

  bg4.append($('<span></span>',{html:'&nbsp;&nbsp;&nbsp;'}))
  .append($('<a></a>',{text:'home page',href:'http://code.google.com/p/color-model-conversion/'}))
  .append($('<br/>'))
  .append($('<span></span>').html('<input type="radio" name="change" checked="true"/>').children().attr({id:'group',title:'Only when group fields (e.g. red, gree and blue field) all are modified, other fields change.'}).click(function(){
    groupChange = true;
  }).change(function(){
    changeAllToFalse();
    readyRGBChange[0] = false;
    readyRGBChange[1] = false;
    readyRGBChange[2] = false;
  })).append($('<label></label>',{htmlFor:'group',text:'group change',title:'Only when group fields (e.g. red, gree and blue field) all are modified, other fields change.'}))
  .append($('<span></span>',{text:'  '}))
  .append($('<span></span>').html('<input type="radio" name="change" />').children().attr({id:'single',title:'When a field has been modified, other fields immediately change.'}).click(function(){
    groupChange = false;
  }).change(function(){
    changeAllToFalse();
    readyRGBChange[0] = false;
    readyRGBChange[1] = false;
    readyRGBChange[2] = false;
  })).append($('<label></label>',{htmlFor:'single',text:'single field change',title:'When a field has been modified, other fields immediately change.'}))
  .append($('<br/>'))
  .append($('<span></span>',{text:'change from: '}))
  .append($('<button></button>',{text:'many colors'}).click(function(){
    slientError = true;
    changeAllToFalse();
    fillColor();
    slientError = false;
  }))
  .append($('<button></button>',{text:'hex color'}).click(function(){
    slientError = true;
    for(var i = 0; i < 14; ++i){
      changeOtherToFalse(i,"hex");
      changeFromHex.apply($('#input'+i)[0],arguments);
    }
    slientError = false;
  }))
  .append($('<button></button>',{text:'red green blue'}).click(function(){
    slientError = true;
    for(var i = 0; i < 14; ++i){
      changeOtherToFalse(i,"upper");
      changeFromUpper.apply($('#red'+i)[0],arguments);
    }
    slientError = false;
  }))
  .append($('<button></button>',{text:'HS luma',id:'lower'}).click(function(){
    slientError = true;
    for(var i = 0; i < 14; ++i){
      changeOtherToFalse(i,"lower");
      changeFromLower.apply($('#out0'+i)[0],arguments);
    }
    slientError = false;
  }))
  .append($('<br/>'))
  .append($('<span></span>',{text:'color model: '}))
  .append($('<span></span>').html('<input type="radio" name="model"/>').children().attr({id:'HSV'}).change(function(){
    colorModel = "HSV"
    convert = RGBtoHSV;
    antiConvert = HSVtoRGB;
    $('#out2label').text('value');
    $('#out6label').text('value');
    $('#lower').text('HSV');
    $('#yes').attr({disabled:'true'});
    $('#no').attr({disabled:'true'});
    $('#HSI').attr({disabled:'true'});
    $('#GIMP').attr({disabled:'true'});
    $('#custom').attr({disabled:'true'});
    $('#changeNow').attr({disabled:'true'});
    $('#r').attr({disabled:'true'});
    $('#g').attr({disabled:'true'});
    $('#b').attr({disabled:'true'});
    updateChange();
  })).append($('<label></label>',{htmlFor:'HSV',text:'HSV'}))
  .append($('<span></span>',{text:' '}))
  .append($('<span></span>').html('<input type="radio" name="model"/>').children().attr({id:'HSL'}).change(function(){
    colorModel = "HSL"
    convert = RGBtoHSL;
    antiConvert = HSLtoRGB;
    $('#out2label').text('lightness');
    $('#out6label').text('lightness');
    $('#lower').text('HSL');
    $('#yes').attr({disabled:'true'});
    $('#no').attr({disabled:'true'});
    $('#HSI').attr({disabled:'true'});
    $('#GIMP').attr({disabled:'true'});
    $('#custom').attr({disabled:'true'});
    $('#changeNow').attr({disabled:'true'});
    $('#r').attr({disabled:'true'});
    $('#g').attr({disabled:'true'});
    $('#b').attr({disabled:'true'});
    updateChange();
  })).append($('<label></label>',{htmlFor:'HSL',text:'HSL'}))
  .append($('<span></span>',{text:' '}))
  .append($('<span></span>').html('<input type="radio" name="model" checked="true"/>').children().attr({id:'HSY',title:'luma = r * red + g * green + b * blue'}).change(function(){
    colorModel = "HSY"
    convert = realRGBtoHSY;
    antiConvert = realHSYtoRGB;
    $('#out2label').text('luma');
    $('#out6label').text('luma');
    $('#lower').text('HS luma');
    $('#yes').removeAttr('disabled');
    $('#no').removeAttr('disabled');
    $('#HSI').removeAttr('disabled');
    $('#GIMP').removeAttr('disabled');
    $('#custom').removeAttr('disabled');
    if(customChange){
      $('#changeNow').removeAttr('disabled');
      $('#r').removeAttr('disabled');
      $('#g').removeAttr('disabled');
      $('#b').removeAttr('disabled');
    }
    updateChange();
  })).append($('<label></label>',{htmlFor:'HSY',text:'HS luma',title:'luma = r * red + g * green + b * blue'}))
  .append($('<span></span>',{html:'&nbsp;&nbsp;&nbsp;'}))
  .append($('<span></span>',{text:'cylinder model shape:'}))
  .append($('<span></span>').html('<input type="radio" name="shape" checked="true"/>').children().attr({id:'yes'}).change(function(){
    isCylinder = true;
  })).append($('<label></label>',{htmlFor:'yes',text:'yes'}))
  .append($('<span></span>').html('<input type="radio" name="shape"/>').children().attr({id:'no'}).change(function(){
    isCylinder = false;
  })).append($('<label></label>',{htmlFor:'yes',text:'no'}))
  .append($('<fieldset></fieldset>').css({padding:'0px 4px 4px'})
    .append($('<legend></legend>',{text:'luma of HS luma is'}))
    .append($('<span></span>').html('<input type="radio" name="lumaType"/>').children().attr({id:'HSI'}).change(function(){
      customChange = false;
      r = new Rational(1,3);
      g = new Rational(1,3);
      b = new Rational(1,3);
      $('#current').text('r = ' + r.toString() + ', g = ' + g.toString() + ', b = ' + b.toString());
      $('#changeNow').attr({disabled:'true'});
      $('#r').attr({disabled:'true'});
      $('#g').attr({disabled:'true'});
      $('#b').attr({disabled:'true'});
      updateChangeIfHSY();
    })).append($('<label></label>',{htmlFor:'HSI',text:'HSI'}))
    .append($('<span></span>',{text:' '}))
    .append($('<span></span>').html('<input type="radio" name="lumaType" checked="true"/>').children().attr({id:'GIMP'}).change(function(){
      customChange = false;
      r = new Rational(21,100);
      g = new Rational(72,100);
      b = new Rational(7,100);
      $('#current').text('r = ' + r.toString() + ', g = ' + g.toString() + ', b = ' + b.toString());
      $('#changeNow').attr({disabled:'true'});
      $('#r').attr({disabled:'true'});
      $('#g').attr({disabled:'true'});
      $('#b').attr({disabled:'true'});
      updateChangeIfHSY();
    })).append($('<label></label>',{htmlFor:'GIMP',text:'GIMP Luminosity'}))
    .append($('<span></span>',{text:' '}))
    .append($('<span></span>').html('<input type="radio" name="lumaType"/>').children().attr({id:'custom'}).change(function(){
      customChange = true;
      lastSuccessRGBUpdate();
      readyRGBChange[0] = false;
      readyRGBChange[1] = false;
      readyRGBChange[2] = false;
      if(!inChangeNow && autoChange){
        if(!groupChange){
          readyRGBChange[0] = true;
          readyRGBChange[1] = true;
          readyRGBChange[2] = true;
          changeFromRGB();
        }
        updateChangeIfHSY();
      }
      $('#changeNow').removeAttr('disabled');
      $('#r').removeAttr('disabled');
      $('#g').removeAttr('disabled');
      $('#b').removeAttr('disabled');
    })).append($('<label></label>',{htmlFor:'custom',text:'custom'}))
    .append('<br />')
    .append($('<span></span>',{text:'current: '}))
    .append($('<span></span>',{id:'current',text:'r = 21 / 100, g = 72 / 100, b = 7 / 100'}))
    .append($('<fieldset></fieldset>').css({width:'25em',padding:'0px 4px 4px'})
      .append($('<legend></legend>',{text:'custom'}))
      .append($('<button></button>',{text:'change now',id:'changeNow',disabled:'true'}).click(function(){
        inChangeNow = true;
        $('#custom').click();
        inChangeNow = false;
        changeFromRGB();
        readyRGBChange[0] = false;
        readyRGBChange[1] = false;
        readyRGBChange[2] = false;
        updateChangeIfHSY();
      }))
      .append($('<input />',{type:'text',id:'r',value:'21 / 100',disabled:'true'}).css({width:'7em'}).change(RGBChange))
      .append($('<input />',{type:'text',id:'g',value:'72 / 100',disabled:'true'}).css({width:'7em'}).change(RGBChange))
      .append($('<input />',{type:'text',id:'b',value:'7 / 100',disabled:'true'}).css({width:'7em'}).change(RGBChange))
      .append($('<br/>'))
    )
  );

  var isCylinder = true;

//  var r = new Rational(1,3);
//  var r = new Rational(298,1000);
  var r = new Rational(21,100);

//  var g = new Rational(1,3);
//  var g = new Rational(588,1000);
  var g = new Rational(72,100);

//  var b = new Rational(1,3);
//  var b = new Rational(114,1000);
  var b = new Rational(7,100);

  function realRGBtoHSY(red,green,blue){
    return RGBtoHSY(r,g,b,red,green,blue,isCylinder);
  }

  function realHSYtoRGB(H,S,luma){
    return HSYtoRGB(r,g,b,H,S,luma,isCylinder);
  }

  var convert = realRGBtoHSY;
  var antiConvert = realHSYtoRGB;

  function inputChange(){
    var no = +this.id.match(/\d+/)[0];
    changeOtherToFalse(no,"hex");
    readyChange[no].hex = true;
    if(autoChange)
      changeFromHex.apply(this,arguments);
  }

  var lastSuccess = [];

  var slientError = false;

  function changeFromHex(){
    var no = +this.id.match(/\d+/)[0];
    var val = $(this).val();
    var result = $.trim(val).match(/^\#([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i);
    try{
      if(result){
        var red = new Rational(parseInt(result[1],16),1);
        var green = new Rational(parseInt(result[2],16),1);
        var blue = new Rational(parseInt(result[3],16),1);

        var result = convert(red,green,blue);

        var arcHue = RGBtoArcHue(red,green,blue);

        $('#red' + no).val(red.toString());
        $('#green' + no).val(green.toString());
        $('#blue' + no).val(blue.toString());

        $('#front'+no).css('backgroundColor',val);
        $('#out0'+no).val(result[0].toString());
        $('#out1'+no).val(result[1].toString());
        $('#out2'+no).val(result[2].toString());
        $('#out3'+no).val(arcHue);
        $('#out4'+no).val(result[0].valueOf());
        $('#out5'+no).val(result[1].valueOf());
        $('#out6'+no).val(result[2].valueOf());
        updateFullInput();
        lastSuccess[no][0] = red;
        lastSuccess[no][1] = green;
        lastSuccess[no][2] = blue;

        changeFrom[no] = "hex";

        readyChange[no].hex = false;
      } else {
        throw new InvalidFormatError("invalid format! e.g. of well format: #FF8080");
      }
    }catch(e){
      if(e instanceof InvalidFormatError || e instanceof InvalidRangeError || e instanceof OverflowError){
        readyChange[no].hex = false;
        if(!slientError)
          alert(e.message);
      } else
        throw e;
    }
  }

  var changeFrom = [];
  var readyChange = [];

  function changeOtherToFalse(no,from){
    if(from !== 'upper'){
      readyChange[no].upper[0] = false;
      readyChange[no].upper[1] = false;
      readyChange[no].upper[2] = false;
    }
    if(from !== 'lower'){
      readyChange[no].lower[0] = false;
      readyChange[no].lower[1] = false;
      readyChange[no].lower[2] = false;
    }
  }

  function changeAllToFalse(){
    for(var i = 0; i < 14; ++i)
      changeOtherToFalse(i,'');
  }

  for(var i = 0; i < 14; ++i){
    lastSuccess[i] = [undefined,undefined,undefined];
    changeFrom[i] = '';
    readyChange[i] = {hex:false,upper:[false,false,false],lower:[false,false,false]};
    $('<input />',{type:'text',id:'input'+i}).css({width:'4.7em',position:'absolute',top:'2em',left:i*4.7+'em'}).appendTo(bg2).change(inputChange);
    $('<input />',{type:'text',id:'red'+i}).css({width:'4.7em',position:'absolute',top:'4em',left:i*4.7+'em'}).appendTo(bg2);
    $('<input />',{type:'text',id:'green'+i}).css({width:'4.7em',position:'absolute',top:'6em',left:i*4.7+'em'}).appendTo(bg2);
    $('<input />',{type:'text',id:'blue'+i}).css({width:'4.7em',position:'absolute',top:'8em',left:i*4.7+'em'}).appendTo(bg2);
    $('<input />',{type:'text',id:'out0'+i}).css({width:'4.7em',position:'absolute',top:'11em',left:i*4.7+'em'}).appendTo(bg2);
    $('<input />',{type:'text',id:'out1'+i}).css({width:'4.7em',position:'absolute',top:'13em',left:i*4.7+'em'}).appendTo(bg2);
    $('<input />',{type:'text',id:'out2'+i}).css({width:'4.7em',position:'absolute',top:'15em',left:i*4.7+'em'}).appendTo(bg2);
    $('<input />',{type:'text',id:'out3'+i,readonly:'true'}).css({width:'4.7em',position:'absolute',top:'18em',left:i*4.7+'em'}).appendTo(bg2);
    $('<input />',{type:'text',id:'out4'+i,readonly:'true'}).css({width:'4.7em',position:'absolute',top:'20em',left:i*4.7+'em'}).appendTo(bg2);
    $('<input />',{type:'text',id:'out5'+i,readonly:'true'}).css({width:'4.7em',position:'absolute',top:'22em',left:i*4.7+'em'}).appendTo(bg2);
    $('<input />',{type:'text',id:'out6'+i,readonly:'true'}).css({width:'4.7em',position:'absolute',top:'24em',left:i*4.7+'em'}).appendTo(bg2);
    $('<div></div>',{id:'front'+i}).css({width:'16px',height:'192px',position:'absolute',top:'0px',left:32+i*16+'px'}).appendTo(bg);
  }

  function changeFromUpper(){
    var no = +this.id.match(/(\d+)/)[1];
    var red = $.trim($('#red'+no).val()).match(/^(\d+)\s*\/\s*(\d+)$/);
    var green = $.trim($('#green'+no).val()).match(/^(\d+)\s*\/\s*(\d+)$/);
    var blue = $.trim($('#blue'+no).val()).match(/^(\d+)\s*\/\s*(\d+)$/);
    try{
      if(red && green && blue){
        var overflow0 = checkStringOverflow(red[1],red[2]);
        var overflow1 = checkStringOverflow(green[1],green[2]);
        var overflow2 = checkStringOverflow(blue[1],blue[2]);
        if(overflow0 || overflow1 || overflow2){
          var idno = overflow0 ? 0 : overflow1 ? 1 : 2;
          var e = new OverflowError("overflow of " + ['red','green','blue'][idno] + "!");
          e.nos = [!overflow0,!overflow1,!overflow2];
          throw e;
        }
        var red = new Rational(+red[1],+red[2]);
        var green = new Rational(+green[1],+green[2]);
        var blue = new Rational(+blue[1],+blue[2]);
        var redInt = Rational.roundInt(red);
        var greenInt = Rational.roundInt(green);
        var blueInt = Rational.roundInt(blue);
        var result = convert(red,green,blue);
        var arcHue = RGBtoArcHue(red,green,blue);
        var val = "#" + redInt.toString(16).toUpperCase().replace(/^\d$/,"0$&") + greenInt.toString(16).toUpperCase().replace(/^\d$/,"0$&") + blueInt.toString(16).toUpperCase().replace(/^\d$/,"0$&");
        $('#front'+no).css('backgroundColor',val);
        $('#input'+no).val(val);
        $('#out0'+no).val(result[0].toString());
        $('#out1'+no).val(result[1].toString());
        $('#out2'+no).val(result[2].toString());
        $('#out3'+no).val(arcHue);
        $('#out4'+no).val(result[0].valueOf());
        $('#out5'+no).val(result[1].valueOf());
        $('#out6'+no).val(result[2].valueOf());
        updateFullInput();
        lastSuccess[no][0] = red;
        lastSuccess[no][1] = green;
        lastSuccess[no][2] = blue;
        changeFrom[no] = "upper";
        var upper = readyChange[no].upper;
        upper[0] = false;
        upper[1] = false;
        upper[2] = false;
      } else {
        var text = !red ? 'red' : !green ? 'green' : 'blue';
        var e = new InvalidFormatError("invalid format of " + text + "! e.g. of well format: 255 / 2");
        e.nos = [!!red,!!green,!!blue];
        throw e;
      }
    }catch(e){
      if(e instanceof InvalidFormatError || e instanceof InvalidRangeError || e instanceof OverflowError){
        if(e.nos){
          var upper = readyChange[no].upper;
          upper[0] = e.nos[0];
          upper[1] = e.nos[1];
          upper[2] = e.nos[2];
        }
        if(!slientError)
          alert(e.message);
      } else
        throw e;
    }
  }

  function rgbColor(){
    var no = +this.id.match(/(\d+)/)[1];
    changeOtherToFalse(no,"upper");
    var id = this.id;
    var upper = readyChange[no].upper;
    var idno = id.match(/red/) ? 0 : id.match(/green/) ? 1 : 2;
    upper[idno] = true;
    if(autoChange && (!groupChange || upper[0] && upper[1] && upper[2]))
      changeFromUpper.apply(this,arguments);
  }
  $('input[id^=red]').change(rgbColor);
  $('input[id^=green]').change(rgbColor);
  $('input[id^=blue]').change(rgbColor);

  function changeFromLower(){
    var no = +this.id.match(/\d(\d+)/)[1];
    var val0 = $('#out0'+no).val();
    var result0 = $.trim(val0).match(/^(\d+)\s*\/\s*(\d+)$/);
    var val1 = $('#out1'+no).val();
    var result1 = $.trim(val1).match(/^(\d+)\s*\/\s*(\d+)$/);
    var val2 = $('#out2'+no).val();
    var result2 = $.trim(val2).match(/^(\d+)\s*\/\s*(\d+)$/);
    try{
      if(result0 && result1 && result2){
        var overflow0 = checkStringOverflow(result0[1],result0[2]);
        var overflow1 = checkStringOverflow(result1[1],result1[2]);
        var overflow2 = checkStringOverflow(result2[1],result2[2]);
        if(overflow0 || overflow1 || overflow2){
          var idno = overflow0 ? 0 : overflow1 ? 1 : 2;
          var e = new OverflowError("overflow of " + $('#out'+idno+'label').text() + "!");
          e.nos = [!overflow0,!overflow1,!overflow2];
          throw e;
        }
        var arg0 = new Rational(+result0[1],+result0[2]);
        var arg1 = new Rational(+result1[1],+result1[2]);
        var arg2 = new Rational(+result2[1],+result2[2]);
        var result = antiConvert(arg0,arg1,arg2);
        var redInt = Rational.roundInt(result[0]);
        var greenInt = Rational.roundInt(result[1]);
        var blueInt = Rational.roundInt(result[2]);
        var arcHue = RGBtoArcHue(result[0],result[1],result[2]);
        var val = "#" + redInt.toString(16).toUpperCase().replace(/^\d$/,"0$&") + greenInt.toString(16).toUpperCase().replace(/^\d$/,"0$&") + blueInt.toString(16).toUpperCase().replace(/^\d$/,"0$&");
        $('#front'+no).css('backgroundColor',val);
        $('#input'+no).val(val);
        $('#red' + no).val(result[0].toString());
        $('#green' + no).val(result[1].toString());
        $('#blue' + no).val(result[2].toString());
        $('#out3'+no).val(arcHue);
        $('#out4'+no).val(arg0.valueOf());
        $('#out5'+no).val(arg1.valueOf());
        $('#out6'+no).val(arg2.valueOf());
        updateFullInput();
        lastSuccess[no][0] = result[0];
        lastSuccess[no][1] = result[1];
        lastSuccess[no][2] = result[2];
        changeFrom[no] = "lower";
        var lower = readyChange[no].lower;
        lower[0] = false;
        lower[1] = false;
        lower[2] = false;
      } else {
        var idno = !result0 ? 0 : !result1 ? 1 : 2;
        var e = new InvalidFormatError("invalid format of " + $('#out'+idno+'label').text() + "! e.g. of well format: 3 / 5");
        e.nos = [!!result0,!!result1,!!result2];
        throw e;
      }
    }catch(e){
      if(e instanceof InvalidFormatError || e instanceof InvalidRangeError || e instanceof OverflowError){
        if(e.nos){
          var lower = readyChange[no].lower;
          lower[0] = e.nos[0];
          lower[1] = e.nos[1];
          lower[2] = e.nos[2];
        }
        if(!slientError)
          alert(e.message);
      } else
        throw e;
    }
  }

  function outChange(){
    var no = +this.id.match(/\d(\d+)/)[1];
    changeOtherToFalse(no,"lower");
    var id = this.id;
    var lower = readyChange[no].lower;
    lower[+id.match(/out(\d)/)[1]] = true;
    if(autoChange && (!groupChange || lower[0] && lower[1] && lower[2]))
      changeFromLower.apply(this,arguments);
  }
  $('input[id^=out0]').change(outChange);
  $('input[id^=out1]').change(outChange);
  $('input[id^=out2]').change(outChange);
});


</script>
</body>
</html>